﻿
Partial Public Class operationJobConsumer
    Public Class constants
        Public Const environmentVariable_jobId As String = "OPERATIONJOBCONSUMER_JOBID"
        Public Const finished_percentage As Int64 = 10000
    End Class

    Private Class job
        Private Const processOutputSuffix As String = ".output"
        Private WithEvents proc As Process = Nothing
        Private ReadOnly _id As String = Nothing
        Private ReadOnly _percentage As atomicInt64 = Nothing
        Private ReadOnly _startMs As Int64 = 0
        Private ReadOnly processOutput As String = Nothing

        Public Sub New(ByVal id As String, ByVal app As String, ByVal arg As String)
            assert(Not isemptystring(id))
            assert(Not isemptystring(app))
            processOutput = combinepath(processOutputDirectory, strcat(id, processOutputSuffix))
            copy(_id, id)
            startprocess(proc, combinepath(delegateDirectory, app), arg,
                         Function(ByRef p As Process) As Boolean
                             p.StartInfo.RedirectStandardOutput = True
                             p.StartInfo.RedirectStandardError = True
                             Return True
                         End Function,
                         constants.environmentVariable_jobId, id)
            If proc Is Nothing Then
                _percentage = New atomicInt64(npos)
                _startMs = npos
                exited() = True
            Else
                _percentage = New atomicInt64(0)
                _startMs = Now.milliseconds()
                proc.BeginOutputReadLine()
                proc.BeginErrorReadLine()
            End If
        End Sub

        Public Function id() As String
            Return _id
        End Function

        Public Function percentage() As atomicInt64
            Return _percentage
        End Function

        Public Function startMs() As Int64
            Return _startMs
        End Function

        Private _exited As Boolean = False
        Public Property exited() As Boolean
            Get
                Return _exited
            End Get
            Private Set(ByVal value As Boolean)
                assert(Not _exited AndAlso value)
                _exited = value
            End Set
        End Property

        Private _exitMs As Int64 = npos
        Public Property exitMs() As Int64
            Get
                Return _exitMs
            End Get
            Private Set(ByVal value As Int64)
                assert(_exitMs = npos AndAlso value > npos)
                _exitMs = value
            End Set
        End Property

        Private Const extcode_init_value As Int64 = 123456787654321
        Private _extcode As Int64 = extcode_init_value
        Public Property extcode() As Int64
            Get
                Return _extcode
            End Get
            Private Set(ByVal value As Int64)
                assert(_extcode = extcode_init_value)
                _extcode = value
            End Set
        End Property

        Private Sub receive(ByVal e As DataReceivedEventArgs)
            Try
                SyncLock proc
                    appendfile(processOutput, e.Data() + newline.incode())
                End SyncLock
            Catch ex As Exception
                raiseError(errorHandle.errorType.warning,
                           "cannot write process ", processFilename(proc), character.colon, processId(proc),
                           " output to file ", processOutput,
                           ", jobid ", id(), ", ex ", ex.Message)
            End Try
        End Sub

        Private Sub proc_ErrorDataReceived(ByVal sender As Object,
                                            ByVal e As DataReceivedEventArgs) Handles proc.ErrorDataReceived
            receive(e)
        End Sub

        Private Sub proc_Exited(ByVal sender As Object, ByVal e As EventArgs) Handles proc.Exited
            If Not exited() Then
                exited() = True
                Dim exitticks As Int64 = 0
                If processExitTime(proc, exitticks) Then
                    exitMs() = ticksToMilliseconds(exitticks)
                Else
                    exitMs() = Now.milliseconds()
                End If
                extcode() = processExitCode(proc)
                percentage().set(constants.finished_percentage)
            End If
        End Sub

        Private Sub proc_OutputDataReceived(ByVal sender As Object,
                                             ByVal e As DataReceivedEventArgs) Handles proc.OutputDataReceived
            receive(e)
        End Sub

        Protected Overrides Sub Finalize()
            If Not proc Is Nothing Then
                proc.Dispose()
                proc = Nothing
            End If
            GC.KeepAlive(proc)
            MyBase.Finalize()
        End Sub
    End Class
End Class
